/*
* This file is a part of the open-eBackup project.
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
* If a copy of the MPL was not distributed with this file, You can obtain one at
* http://mozilla.org/MPL/2.0/.
*
* Copyright (c) [2024] Huawei Technologies Co.,Ltd.
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*/
#include "PSClient.h"
#include <common/Structs.h>
#include <common/Constants.h>
#include "protect_engines/hyperv/utils/executor/WinCmdExector.h"

using VirtPlugin::SUCCESS;
using namespace HyperVPlugin::Utils;

namespace {
const std::string BIN_DIR = Module::EnvVarManager::GetInstance()->GetAgentHomePath() +
    "\\DataBackup\\ProtectClient\\Plugins\\VirtualizationPlugin\\bin";
const std::string EXECUTOR_NAME = "Executor.ps1";
const DWORD TIMEOUT_DEFAULT = 300;
const std::string CMD_TYPE_CREATE_VM_CHECKPOINT = "CreateVMCheckPoint";
const std::string CMD_TYPE_LIST_DISK = "ListDisk";
const std::string CMD_TYPE_CONVERT_VM_CHECKPOINT = "ConvertVMCheckPoint";
const std::string CMD_TYPE_GET_VM_INFO = "GetVMInfo";
const std::string CMD_TYPE_GET_VM_DRIVER = "GetVMDriver";
const std::string CMD_TYPE_DELETE_REFERPOINT_EXCEPT = "DeleteVMReferrencePointExcept";
const std::string CMD_TYPE_CREATE_VHD = "CreateVHD";
const std::string CMD_TYPE_DELETE_CHECKPOINT = "DeleteVMCheckPoint";
const std::string CMD_TYPE_ADD_TO_CLUSTER = "AddCluster";
const std::string CMD_TYPE_GET_CLUSTER_SHARED_VOLUME = "GetClusterSharedVolume";
}

std::shared_ptr<CreateSnapshotResponse> PSClient::CreateCheckPoint(CreateSnapshotResquest &request)
{
    LOGGUARD("");
    Json::Value param;
    Auth auth;
    param["vmid"] = request.m_vmId;
    param["consistencyLevel"] = request.m_consistencyLevel;
    auth.m_authKey = request.m_authKey;
    auth.m_authPwd = request.m_authPwd;
    CreateSnapshotResponse response;
    ActionResultRes result = Executor<CreateSnapshotResponse>(CMD_TYPE_CREATE_VM_CHECKPOINT, response, auth, param);
    if (result.m_code != SUCCESS) {
        ERRLOG("Create check point failed, code=%d", result.m_code);
        return nullptr;
    }
    return std::make_shared<CreateSnapshotResponse>(response);
}

std::shared_ptr<GetVMVolumesResponse> PSClient::GetVMVolumes(GetVMVolumesRequest &request)
{
    Json::Value param;
    Auth auth;
    param["VMId"] = request.m_vmId;
    auth.m_authKey = request.m_authKey;
    auth.m_authPwd = request.m_authPwd;
    GetVMVolumesResponse response;
    ActionResultRes result = Executor<GetVMVolumesResponse>(CMD_TYPE_LIST_DISK, response, auth, param);
    if (result.m_code != SUCCESS) {
        ERRLOG("Get vm volumes failed, code=%d", result.m_code);
        return nullptr;
    }
    for (auto& item : response.m_diskList) {
        std::transform(item.m_diskIdentifier.begin(), item.m_diskIdentifier.end(), item.m_diskIdentifier.begin(),
            [](unsigned char c) { return ::tolower(c); });
    }
    return std::make_shared<GetVMVolumesResponse>(response);
}

std::shared_ptr<ReferrencePointResponse> PSClient::ConvertCheckPoint(ReferrencePointResquest &request)
{
    LOGGUARD("");
    Json::Value param;
    Auth auth;
    param["vmid"] = request.m_vmId;
    param["checkPointName"] = request.m_checkPointName;
    auth.m_authKey = request.m_authKey;
    auth.m_authPwd = request.m_authPwd;
    ReferrencePointResponse response;
    ActionResultRes result = Executor<ReferrencePointResponse>(CMD_TYPE_CONVERT_VM_CHECKPOINT, response, auth, param);
    if (result.m_code != SUCCESS) {
        ERRLOG("Convert check point failed, code=%d", result.m_code);
        return nullptr;
    }
    return std::make_shared<ReferrencePointResponse>(response);
}

std::shared_ptr<GetVMInfoResponse> PSClient::GetVmInfo(GetVMInfoRequest &request)
{
    LOGGUARD("");
    Json::Value param;
    param["VMId"] = request.m_vmId;
    Auth auth;
    auth.m_authKey = request.m_authKey;
    auth.m_authPwd = request.m_authPwd;
    GetVMInfoResponse response;
    ActionResultRes result = Executor<GetVMInfoResponse>(CMD_TYPE_GET_VM_INFO, response, auth, param);
    if (result.m_code != SUCCESS) {
        ERRLOG("GetVmInfo failed, retRes=%d", result.m_code);
        return nullptr;
    }
    return std::make_shared<GetVMInfoResponse>(response);
}

std::shared_ptr<GetVMHardDiskDriveResponse> PSClient::GetVmDrive(GetVMHardDiskDriveRequest &request)
{
    LOGGUARD("");
    Json::Value param;
    param["VMId"] = request.m_vmId;
    Auth auth;
    auth.m_authKey = request.m_authKey;
    auth.m_authPwd = request.m_authPwd;
    GetVMHardDiskDriveResponse response;
    ActionResultRes result = Executor<GetVMHardDiskDriveResponse>(CMD_TYPE_GET_VM_DRIVER, response, auth, param);
    if (result.m_code != SUCCESS) {
        ERRLOG("GetVm drive failed, retRes=%d", result.m_code);
        return nullptr;
    }
    return std::make_shared<GetVMHardDiskDriveResponse>(response);
}

bool PSClient::DeleteReferPointExcept(ReferrencePointResquest &request)
{
    Json::Value param;
    param["vmid"] = request.m_vmId;
    param["instanceid"] = request.m_instanceId;
    Auth auth;
    auth.m_authKey = request.m_authKey;
    auth.m_authPwd = request.m_authPwd;
    ReferrencePointResponse response;
    ActionResultRes result = Executor<ReferrencePointResponse>(CMD_TYPE_DELETE_REFERPOINT_EXCEPT,
        response, auth, param);
    if (result.m_code != SUCCESS) {
        ERRLOG("Convert check point failed, code=%d", result.m_code);
        return false;
    }
    return true;
}

std::shared_ptr<CreateVHDResponse> PSClient::CreateVHD(CreateVHDRequest &request)
{
    LOGGUARD("");
    Json::Value param;
    param["path"] = request.m_path;
    param["size"] = request.m_size;
    param["type"] = request.m_type;
    param["volumeType"] = request.m_volumeType;
    Auth auth;
    auth.m_authKey = request.m_authKey;
    auth.m_authPwd = request.m_authPwd;
    CreateVHDResponse response;
    ActionResultRes result = Executor<CreateVHDResponse>(CMD_TYPE_CREATE_VHD, response, auth, param);
    if (result.m_code != SUCCESS) {
        ERRLOG("Create vhd failed, retRes=%d", result.m_code);
        return nullptr;
    }
    return std::make_shared<CreateVHDResponse>(response);
}

std::shared_ptr<GetClusterSharedVolumeResponse> PSClient::GetClusterSharedVolume(Auth &auth)
{
    Json::Value param;
    GetClusterSharedVolumeResponse response;
    ActionResultRes result = Executor<GetClusterSharedVolumeResponse>(
        CMD_TYPE_GET_CLUSTER_SHARED_VOLUME, response, auth, param);
    if (result.m_code != SUCCESS) {
        ERRLOG("GetClusterSharedVolume failed, retRes=%d", result.m_code);
        return nullptr;
    }
    return std::make_shared<GetClusterSharedVolumeResponse>(response);
}

bool PSClient::IsAddClusterSuccess(Auth &auth, const std::string &vmId)
{
    Json::Value param;
    param["VMId"] = vmId;
    ActionResultRes response;
    ActionResultRes result = Executor<ActionResultRes>(CMD_TYPE_ADD_TO_CLUSTER, response, auth, param);
    return result.m_code == SUCCESS && response.m_code == SUCCESS;
}

template<typename T>
ActionResultRes PSClient::Executor(const std::string &command, T &res,
    Auth &auth, const Json::Value &param)
{
    LOGGUARD("");
    Param prm;
    prm.scriptDir = BIN_DIR;
    prm.timeout = TIMEOUT_DEFAULT;
    prm.cmdType = command;
    prm.scriptName = EXECUTOR_NAME;
    prm.userName = auth.m_authKey;
    prm.password = auth.m_authPwd;
    prm.param = param;

    Json::Value result;
    ActionResultRes actionResult;
    WinCmdExector execaller;
    execaller.SetTargetHost("localhost");
    int retRes = execaller.Execute(prm, result);
    if (retRes != SUCCESS) {
        Module::JsonHelper::JsonValueToStruct(result, actionResult);
        ERRLOG("Run executor failed, retRes=%d, code:%d", retRes, actionResult.m_code);
        return actionResult;
    }
    DBGLOG("Call execute command success.");
    Module::JsonHelper::JsonValueToStruct(result, res);
    return actionResult;
}

bool PSClient::CheckResultValid(const Json::Value &result)
{
    if (result.empty() ||
        !result.isMember("result") ||
        !result["result"].isArray() ||
        result["result"].size() <= 0 ||
        result["result"][0].size() <= 0) {
        return false;
    }
    return true;
}

bool PSClient::DeleteSpecificSnapshot(DeleteSnapshotResquest &request)
{
    LOGGUARD("");
    Json::Value param;
    Auth auth;
    param["vmid"] = request.m_vmId;
    param["vmName"] = request.m_vmName;
    param["snapShotName"] = request.m_checkPointName;
    param["snapShotId"] = request.m_checkPointInstanceId;

    auth.m_authKey = request.m_authKey;
    auth.m_authPwd = request.m_authPwd;

    DeleteSnapshotResponse response;
    ActionResultRes result = Executor<DeleteSnapshotResponse>(CMD_TYPE_DELETE_CHECKPOINT,
        response, auth, param);
    if (result.m_code != SUCCESS) {
        ERRLOG("DeleteSpecificSnapshot, code=%d", result.m_code);
        return false;
    }
    return true;
}